package com.lll.nettywebsocket.endpoint;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSONObject;
import com.lll.nettywebsocket.common.Constants;
import com.lll.nettywebsocket.pojo.Message;
import com.lll.nettywebsocket.repository.WebsocketSessionRepository;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.MultiValueMap;
import org.yeauty.annotation.*;
import org.yeauty.pojo.Session;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@ServerEndpoint(path = "/ws/{cid}", port = "80")
@Slf4j
public class MyWebSocket {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WebsocketSessionRepository websocketSessionRepository;

    @BeforeHandshake
    public void handshake(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String cid, @PathVariable Map pathMap) {
        session.setSubprotocols("stomp");
        // 认证鉴权
        //        if (!"ok".equals(req)) {
        //            System.out.println("Authentication failed!");
        //            session.close();
        //        }
        session.setAttribute("cid", cid);
    }

    @OnOpen
    public void onOpen(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String cid, @PathVariable Map pathMap) {
        log.info("connection established, cid : [{}]", cid);
        websocketSessionRepository.addSession(session);
    }

    @OnClose
    public void onClose(Session session) throws IOException {
        log.info("connection closed, cid : [{}]", (String) session.getAttribute("cid"));
        websocketSessionRepository.removeSession(session);
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        throwable.printStackTrace();
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        Message messageObj = JSONObject.parseObject(message, Message.class);
        if (Objects.isNull(messageObj)) {
            return;
        }
        /**
         *
         * {  "cid":"1001","to":"1001","content":"hello 1001"}
         */
        log.info("onMessage {}", JSONObject.toJSONString(messageObj));

        if ("all".equals(messageObj.getTo())) {
            // 群发广播
            List<Session> sessions = websocketSessionRepository.findAllSessions();
            if (CollUtil.isNotEmpty(sessions)) {
                // 连接在了本台服务器
                for (Session s : sessions) {
                    s.sendText(messageObj.getContent());
                }
            }
            String cid = messageObj.getCid();
            List<Session> sessionList = websocketSessionRepository.findSession(cid);
            if (CollUtil.isNotEmpty(sessionList)) {
                // 连接到其他服务器，通知其他节点处理
                log.info("broadcast .....");
                redisTemplate.convertAndSend(Constants.REDIS_CHANNEL_NAME, message);
            }


        } else {
            // 单发
            List<Session> sessions = websocketSessionRepository.findSession(messageObj.getTo());
            if (CollUtil.isNotEmpty(sessions)) {
                // 连接在了本台服务器
                for (Session s : sessions) {
                    s.sendText(messageObj.getContent());
                }
            } else {
                // 连接到其他服务器，通知其他节点处理
                log.info("broadcast .....");
                redisTemplate.convertAndSend(Constants.REDIS_CHANNEL_NAME, message);
            }
        }


    }

    @OnBinary
    public void onBinary(Session session, byte[] bytes) {
        for (byte b : bytes) {
            System.out.println(b);
        }
        session.sendBinary(bytes);
    }

    @OnEvent
    public void onEvent(Session session, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            switch (idleStateEvent.state()) {
                case READER_IDLE:
                    System.out.println("read idle");
                    break;
                case WRITER_IDLE:
                    System.out.println("write idle");
                    break;
                case ALL_IDLE:
                    System.out.println("all idle");
                    break;
                default:
                    break;
            }
        }
    }

}